package com.itheima.j2cache.aop;

import com.itheima.j2cache.anno.Cache;
import com.itheima.j2cache.anno.CacheEvictor;
import com.itheima.j2cache.aop.processor.AbstractCacheAnnotationProcessor;
import com.itheima.j2cache.aop.processor.CacheAnnotationProcessor;
import com.itheima.j2cache.aop.processor.CacheEvictorAnnotationProcessor;
import lombok.extern.slf4j.Slf4j;
import net.oschina.j2cache.CacheChannel;
import org.aopalliance.intercept.Interceptor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;

/**
 * 缓存操作拦截器
 *
 * @author llhuang10
 * @date 2021/10/7 22:15
 */
@Slf4j
@Aspect
@Component
// 两种代理 JDK | cglib --> 指定cglib方式为Controller创建代理对象, 代理对象其实是目标对象的子类-> 继承关系
@EnableAspectJAutoProxy(proxyTargetClass = true)
//  为什么? 防止SpringApplicationContextUtils没有被容器扫描到, 之前SpUtils并不在启动类包路径下, 所以需要使用@Import注入一下
// @Import(SpringApplicationContextUtils.class)
public class CacheMethodInterceptor implements Interceptor {

    @Resource
    private CacheChannel cacheChannel;


    /**
     * 拦截单个Cache注解的方法以便实现缓存
     *
     * @param joinPoint 切点
     * @return 结果
     */
    @Around(value = "@annotation(com.itheima.j2cache.anno.Cache)")
    public Object invokeCacheAllMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("-------------缓存拦截器执行了.............");

        // 获取方法前面对象
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取当前拦截到的Controller方法对象
        Method method = signature.getMethod();
        // 获取方法上面的Cache注解信息
        Cache cache = AnnotationUtils.findAnnotation(method, Cache.class);

        if (cache != null) {
            log.info("需要进行设置缓存存数据处理...");
            // 类似抽象工厂的形式, 创建处理器对象, 具体处理缓存逻辑
            CacheAnnotationProcessor processor = AbstractCacheAnnotationProcessor.getProcessor(joinPoint, cache);

            try {
                return processor.process(joinPoint);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }

        log.warn("the method : {} don't have Annotation @Cache", method.getName());
        // 没有获取到Cache注解信息, 直接放行, 这种 joinPoint.proceed() 的异常直接抛?
        return joinPoint.proceed(joinPoint.getArgs());
    }


    /**
     * 拦截CacheEvictor注解的方法以便实现失效指定key的缓存
     *
     * @param joinPoint 切点
     * @return 结果
     */
    @Around(value = "@annotation(com.itheima.j2cache.anno.CacheEvictor)")
    public Object invokeCacheEvictorAllMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("-------------清理缓存拦截器执行了.............");

        // 获取方法前面对象
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取当前拦截到的Controller方法对象
        Method method = signature.getMethod();
        // 获取方法上面的Cache注解信息
        CacheEvictor cacheEvictor = AnnotationUtils.findAnnotation(method, CacheEvictor.class);
        if (cacheEvictor != null) {
            log.info("清理缓存处理...");
            // 创建清理缓存处理器对象, 具体处理缓存逻辑
            CacheEvictorAnnotationProcessor processor = AbstractCacheAnnotationProcessor.getProcessor(joinPoint, cacheEvictor);

            try {
                return processor.process(joinPoint);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }

        log.warn("the method : {} don't have Annotation @CacheEvictor", method.getName());
        // 没有获取到Cache注解信息, 直接放行, 这种 joinPoint.proceed() 的异常直接抛?
        return joinPoint.proceed(joinPoint.getArgs());
    }

}
